package com.stary.mybatisplus3.generator;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.stary.mybatisplus3.generator.rule.DbDriverName;




/**
 * <p>mybatisplus 3.x 自定义代码生成器</p>
 * 目前只支持 mysql，oracle，sqlserver2005以上版本，默认是oracle
 * @author stary
 * @date 2019年3月19日
 */
public class MyBatisPlusGenerator {

	private static final Logger logger = LoggerFactory.getLogger(MyBatisPlusGenerator.class);


	private static final String DEFAULT_TABLE_SEQUENCE_VALUE_SUFFIX = "_SEQ";
	private static final String DEFAULT_TABLE_SEQUENCE_CLAZZ = "Integer";

	private static final String DEFAULT_AUTHOR = "stary";
	private static final String DEFAULT_OUTPUT_DIR = "D:\\";
	private static final String DEFAULT_PACKAGE_NAME = "com.stary";
	private static final String DEFAULT_ENTITY_NAME = "model";
	private static final String DEFAULT_MAPPER_NAME = "mapper";
	private static final String DEFAULT_CONTROLLER_NAME = "controller";

	/*------------------全局配置属性------------------*/
	/**
	 * 全局配置
	 */
	private GlobalConfig global;
	/**
	 * 开发人员
	 */
	private String author = DEFAULT_AUTHOR;
	/**
	 * 生成文件的输出目录，默认 D 盘根目录
	 */
	private String outputDir = DEFAULT_OUTPUT_DIR;
	/**
	 * 生成的注解带上IdType类型，默认INPUT
	 */
	private IdType idType = IdType.INPUT;
	/**
	 * 是否打开生成后的文件夹 默认 false
	 */
	private boolean open = false;

	/*------------------数据库配置属性------------------*/
	/**
	 * 数据库配置
	 */
	private DataSourceConfig dataSource;
	/**
	 * 数据库类型，默认 oracle，根据该属性自动匹配驱动名
	 */
	private DbType dbType = DbType.ORACLE;
	/**
	 * 数据库url
	 */
	private String dbUrl;
	/**
	 * 数据库登录用户名
	 */
	private String dbUsername;
	/**
	 * 数据库登录密码
	 */
	private String dbPassword;
	/**
	 * 数据库所在的机器的名称 ，当dbUrl为空时 必填
	 */
	private String dbHost;
	/**
	 * 数据库端口号，当dbUrl为空时 必填
	 */
	private String dbPort;
	/**
	 * 数据库名，当dbUrl为空时 必填
	 */
	private String dbName;

	/*------------------生成文件以及实体类其注解配置属性------------------*/
	/**
	 * 策略配置
	 */
	private StrategyConfig strategy;
	/**
	 * 包配置
	 */
	private PackageConfig packageInfo;
	/**
	 * 生成的包路径，默认 com.stary
	 */
	private String packageName = DEFAULT_PACKAGE_NAME;
	/**
	 * entity包名，默认model
	 */
	private String entity = DEFAULT_ENTITY_NAME;
	/**
	 * mapper包名，默认 mapper
	 */
	private String mapper = DEFAULT_MAPPER_NAME;
	/**
	 * controller包名 默认 controller
	 */
	private String controller = DEFAULT_CONTROLLER_NAME;
	/**
	 * 需要生成的表名组，默认生成所有表
	 */
	private Set<String> tables;
	/**
	 * 是否去掉生成实体名的前缀组
	 */
	private Set<String> tablePrefix;
	/**
	 * 是否强制带上实体类属性注解
	 */
	private boolean enableTableFieldAnnotation = true;
	/**
	 * 是否强制带上序列注解@KeySequence，相关的设置只有oracle才能生效，默认为true
	 */
	private boolean enableTableSequenceAnnotation = true;
	/**
	 * 【实体】是否生成字段常量（默认 true）
	 */
	private boolean entityColumnConstant = true;
	/**
	 * 自定义设置 {@code @KeySequence} value 前缀 + 表名，如 ：{@code value = SEQ_USER}
	 */
	private String tableSequenceValuePrefix;
	/**
	 * 自定义设置 {@code @KeySequence} value 表名 + 后缀，默认_SEQ  如 ：{@code value = USER_SEQ}
	 */
	private String tableSequenceValueSuffix = DEFAULT_TABLE_SEQUENCE_VALUE_SUFFIX;
	/**
	 * 自定义设置@{@code @KeySequence} 默认{@code Integer} ，如 {@code clazz = Integer.class}
	 */
	private String tableSequenceClazz = DEFAULT_TABLE_SEQUENCE_CLAZZ;

	/**
	 * Service是否带'I'，默认false 设置成true: user -> IUserService
	 */
	private boolean serviceNameStartWithI = false;
	/*------------------自定义模板以及自定义参数配置------------------*/
	/**
	 * 模板配置
	 */
	private TemplateConfig template;
	/**
	 * 参数配置
	 */
	private InjectionConfig injection;
	/**
	 * 是否开启模板 默认true
	 */
	private boolean openTemplate = true;
	/**
	 * 自定义参数
	 */
	private Map<String, Object> params;

	/**
	 * 是否关闭plus模式，true 生成mybatis代码
	 */
	private boolean closePlusMode = false;

	/**
	 * 执行生成
	 */
	public void execute() {
		// 自动生成器
		AutoGenerator generator = new AutoGenerator();
		logger.debug("---------------------->> 加载配置  <<----------------------");
		// 配置生成器
		generator.setGlobalConfig(getGlobal()).setDataSource(getDataSource())
		.setStrategy(getStrategy()).setPackageInfo(getPackageInfo());
		if (openTemplate) {
			generator.setTemplate(getTemplate()).setCfg(getInjection());
		}
		// 执行生成
		generator.execute();
	}

	public void setDbType(DbType dbType) {
		// 不是oracle数据库去掉序列注解
		if(StringUtils.checkValNotNull(dbType) && !DbType.ORACLE.equals(dbType)){
			this.enableTableSequenceAnnotation = false;
		}
		this.dbType = dbType;
	}

	public String getDbUrl() {
		if (StringUtils.checkValNull(dbUrl)) {
			if (StringUtils.checkValNotNull(dbHost) && StringUtils.checkValNotNull(dbPort) && StringUtils.checkValNotNull(dbName)) {
				StringBuffer buffer = new StringBuffer();
				switch (dbType) {
				case ORACLE:
					buffer.append("jdbc:oracle:thin:@").append(dbHost).append(":").append(dbPort).append(":").append(dbName);
					break;
				case MYSQL:
					buffer.append("jdbc:mysql://").append(dbHost).append(":").append(dbPort).append("/").append(dbName);
					break;
				case SQL_SERVER:
					buffer.append("jdbc:microsoft:sqlserver://").append(dbHost).append(":").append(dbPort).append(";DatabaseName=").append(dbName);
					break;
				default:
					break;
				}
			}
		}
		return dbUrl;
	}

	public PackageConfig getPackageInfo() {
		logger.debug("---------------------->> 加载包配置  <<----------------------");
		if (StringUtils.checkValNull(packageInfo)) {
			logger.debug("父包名---->> {}", this.packageName);
			logger.debug("entity包名---->> {}", this.controller);
			logger.debug("mapper包名---->> {}", this.mapper);
			logger.debug("controller层名---->> {}", this.controller);
			packageInfo = new PackageConfig();
			// 父包名
			packageInfo.setParent(packageName);
			// controller包名
			packageInfo.setController(controller);
			// entity包名
			packageInfo.setEntity(entity);
			// mapper包名
			packageInfo.setMapper(mapper);
		}
		return packageInfo;
	}
	public GlobalConfig getGlobal() {
		logger.debug("---------------------->> 加载全局配置  <<----------------------");
		if(global == null) {
			logger.debug("是否关闭plus模式---->> {}", closePlusMode);
			logger.debug("开发人员---->> {}", author);
			logger.debug("生成输出目录---->> {}", outputDir);
			logger.debug("IdType类型---->> {}", this.idType);
			logger.debug("Service是否带'I'---->> {}", this.serviceNameStartWithI);
			global = new GlobalConfig();
			// 不需要ActiveRecord特性的请改为false
			global.setActiveRecord(false);
			// 开发人员
			global.setAuthor(author);
			// 生成文件的输出目录，默认 D 盘根目录
			global.setOutputDir(outputDir);
			// 是否覆盖已有文件
			global.setFileOverride(true);
			// XML 二级缓存
			global.setEnableCache(false);
			// XML ResultMap
			global.setBaseResultMap(true);
			// XML columList
			global.setBaseColumnList(true);
			// 是否打开输出目录 默认true
			global.setOpen(open);
			// 开启序列注解 必须要设置 ID类型为 INPUT
			if(enableTableSequenceAnnotation) {
				global.setIdType(IdType.INPUT);
			}
			// Service是否带'I'
			if (!serviceNameStartWithI) {
				global.setServiceName("%sService");
			}
			// 自定义文件命名，注意 %s 会自动填充表实体属性！
			// global.setMapperName("%sDao");
			// global.setXmlName("%sDao");
			// global.setServiceName("%sService");
			// global.setServiceImplName("%sServiceDiy");
			// global.setControllerName("%sAction");
		}
		return global;
	}

	public DataSourceConfig getDataSource() {
		logger.debug("---------------------->> 加载数据源配置  <<----------------------");
		if ((dataSource == null)) {
			dataSource = new DataSourceConfig();
			logger.debug("数据库类型---->> {}", dbType);
			if (StringUtils.checkValNull(getDbUrl())) {
				throw new GeneratorException("dataSource url is null");
			}
			if (StringUtils.checkValNull(dbUsername)) {
				throw new GeneratorException("dataSource username is null");
			}
			if (StringUtils.checkValNull(dbPassword)) {
				throw new GeneratorException("dataSource password is null");
			}
			dataSource.setDbType(dbType);
			// 驱动名
			String driverName = DbDriverName.ORACLE.getName();
			 // 数据库类型
			switch (dbType) {
			case MYSQL:
				driverName = DbDriverName.MYSQL.getName();
				break;
			case SQL_SERVER:
				driverName = DbDriverName.SQLSERVER.getName();
				break;
			default:
				driverName = DbDriverName.ORACLE.getName();
				break;
			}
			dataSource.setDriverName(driverName);
			logger.debug("数据库驱动----->> {}", driverName);
			logger.debug("数据库url----->> {}", getDbUrl());
			 // 用户名
			dataSource.setUsername(dbUsername);
			 // 密码
			dataSource.setPassword(dbPassword);
			 // 驱动连接的url
			dataSource.setUrl(getDbUrl());
		}
		return dataSource;
	}
	public StrategyConfig getStrategy() {
		logger.debug("---------------------->> 加载策略配置  <<----------------------");
		if (strategy == null) {
			strategy = new StrategyConfig();
			logger.debug("是否生成字段注解---->> {}", enableTableFieldAnnotation);
			logger.debug("是否生成序列注解---->> {}", enableTableSequenceAnnotation);
			if (DbType.ORACLE.equals(dbType)) {
				// ORACLE 注意 全局大写命名全局大写命名
				strategy.setCapitalMode(true);
			}
			if (tablePrefix != null) {
				logger.debug("需要去掉表的前缀---->> {}", tablePrefix.toString());
				strategy.setTablePrefix(tablePrefix.toArray(new String[] {}));
			}
			if (tables != null) {
				logger.debug("需要生成的表名---->> {}", tables.toString());
				 // 需要包含的表名，多个表名传数组
				strategy.setInclude(tables.toArray(new String[] {}));
			} else {
				logger.debug("需要生成的表名---->> *");
			}
			 // 表名生成策略	下划线转驼峰命名
			strategy.setNaming(NamingStrategy.underline_to_camel);
			 // 是否生成实体时，生成字段注解
			strategy.setEntityTableFieldAnnotationEnable(enableTableFieldAnnotation);
			strategy.setEntityColumnConstant(entityColumnConstant);
		}
		return strategy;
	}
	public TemplateConfig getTemplate() {
		logger.debug("---------------------->> 加载自定义模板配置  <<----------------------");
		if (template == null) {
			template = new TemplateConfig();
			template.setEntity("/templates/my_entity.java");
			template.setMapper("/templates/my_mapper.java");
			template.setService("/templates/my_service.java");
			template.setServiceImpl("/templates/my_serviceImpl.java");
			template.setXml("/templates/my_mapper.xml");
		}
		return template;
	}
	public InjectionConfig getInjection() {
		logger.debug("---------------------->> 加载自定义参数配置  <<----------------------");
		if (injection == null) {
			injection = new InjectionConfig() {
				@Override
				public void initMap() {
					if (params == null) {
						params = new HashMap<String, Object>();
					}
					params.put("enableTableSequenceAnnotation", enableTableSequenceAnnotation);
					params.put("tableSequenceValuePrefix", tableSequenceValuePrefix);
					params.put("tableSequenceValueSuffix", tableSequenceValueSuffix);
					params.put("tableSequenceClazz", tableSequenceClazz);
					params.put("closePlusMode", closePlusMode);
					this.setMap(params);
				}
			};
		}
		return injection;
	}



	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getOutputDir() {
		return outputDir;
	}

	public void setOutputDir(String outputDir) {
		this.outputDir = outputDir;
	}

	public IdType getIdType() {
		return idType;
	}

	public void setIdType(IdType idType) {
		this.idType = idType;
	}

	public boolean isOpen() {
		return open;
	}

	public void setOpen(boolean open) {
		this.open = open;
	}

	public String getDbUsername() {
		return dbUsername;
	}

	public void setDbUsername(String dbUsername) {
		this.dbUsername = dbUsername;
	}

	public String getDbPassword() {
		return dbPassword;
	}

	public void setDbPassword(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public String getDbHost() {
		return dbHost;
	}

	public void setDbHost(String dbHost) {
		this.dbHost = dbHost;
	}

	public String getDbPort() {
		return dbPort;
	}

	public void setDbPort(String dbPort) {
		this.dbPort = dbPort;
	}

	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}

	public String getPackageName() {
		return packageName;
	}

	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	public String getEntity() {
		return entity;
	}

	public void setEntity(String entity) {
		this.entity = entity;
	}

	public String getMapper() {
		return mapper;
	}

	public void setMapper(String mapper) {
		this.mapper = mapper;
	}

	public String getController() {
		return controller;
	}

	public void setController(String controller) {
		this.controller = controller;
	}

	public Set<String> getTables() {
		return tables;
	}

	public void setTables(Set<String> tables) {
		this.tables = tables;
	}

	public Set<String> getTablePrefix() {
		return tablePrefix;
	}

	public void setTablePrefix(Set<String> tablePrefix) {
		this.tablePrefix = tablePrefix;
	}

	public boolean isEnableTableFieldAnnotation() {
		return enableTableFieldAnnotation;
	}

	public void setEnableTableFieldAnnotation(boolean enableTableFieldAnnotation) {
		this.enableTableFieldAnnotation = enableTableFieldAnnotation;
	}

	public boolean isEnableTableSequenceAnnotation() {
		return enableTableSequenceAnnotation;
	}

	public void setEnableTableSequenceAnnotation(
			boolean enableTableSequenceAnnotation) {
		this.enableTableSequenceAnnotation = enableTableSequenceAnnotation;
	}

	public String getTableSequenceValuePrefix() {
		return tableSequenceValuePrefix;
	}

	public void setTableSequenceValuePrefix(String tableSequenceValuePrefix) {
		this.tableSequenceValuePrefix = tableSequenceValuePrefix;
	}

	public String getTableSequenceValueSuffix() {
		return tableSequenceValueSuffix;
	}

	public void setTableSequenceValueSuffix(String tableSequenceValueSuffix) {
		this.tableSequenceValueSuffix = tableSequenceValueSuffix;
	}

	public String getTableSequenceClazz() {
		return tableSequenceClazz;
	}

	public void setTableSequenceClazz(String tableSequenceClazz) {
		this.tableSequenceClazz = tableSequenceClazz;
	}

	public boolean isServiceNameStartWithI() {
		return serviceNameStartWithI;
	}

	public void setServiceNameStartWithI(boolean serviceNameStartWithI) {
		this.serviceNameStartWithI = serviceNameStartWithI;
	}

	public boolean isOpenTemplate() {
		return openTemplate;
	}

	public void setOpenTemplate(boolean openTemplate) {
		this.openTemplate = openTemplate;
	}

	public Map<String, Object> getParams() {
		return params;
	}

	public void setParams(Map<String, Object> params) {
		this.params = params;
	}

	public DbType getDbType() {
		return dbType;
	}

	public void setGlobal(GlobalConfig global) {
		this.global = global;
	}

	public void setDataSource(DataSourceConfig dataSource) {
		this.dataSource = dataSource;
	}

	public void setDbUrl(String dbUrl) {
		this.dbUrl = dbUrl;
	}

	public void setStrategy(StrategyConfig strategy) {
		this.strategy = strategy;
	}

	public void setPackageInfo(PackageConfig packageInfo) {
		this.packageInfo = packageInfo;
	}

	public void setTemplate(TemplateConfig template) {
		this.template = template;
	}

	public void setInjection(InjectionConfig injection) {
		this.injection = injection;
	}

	public boolean isClosePlusMode() {
		return closePlusMode;
	}

	public void setClosePlusMode(boolean closePlusMode) {
		if (closePlusMode) {
			this.enableTableFieldAnnotation = false;
		}
		this.closePlusMode = closePlusMode;
	}

	public boolean isEntityColumnConstant() {
		return entityColumnConstant;
	}

	public void setEntityColumnConstant(boolean entityColumnConstant) {
		this.entityColumnConstant = entityColumnConstant;
	}

}
